package com.practice.java;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

public class Practice6SnowFlakeUtil {

    //雪花id结构：第1位符号位 第2-42位时间戳 第43-47位集群id 第48-52位机器id 第53-64位序列号
    private static final int DEFAULT_DATACENTER_ID = 0;
    private static final int DEFAULT_WORKER_ID = 0;
    private static final int DATACENTER_ID_BITS = 5;
    private static final int WORKER_ID_BITS = 5;
    private static final int SEQUENCE_BITS = 12;
    private static final long MAX_DATACENTER_ID = (int) Math.pow(2, DATACENTER_ID_BITS) - 1;
    private static final long MAX_WORKER_ID = (int) Math.pow(2, WORKER_ID_BITS) - 1;
    private static final long MAX_SEQUENCE = (long) Math.pow(2, SEQUENCE_BITS) - 1;
    //2024-09-08 14:54
    private static final long INIT_TIMESTAMP = 1725778442481L;
    private long sequence = 0;
    private long lastTimeMills = 0;

    public synchronized long nextId(long dataCenterId, long workerId) {
        if(dataCenterId < 0 || dataCenterId > MAX_DATACENTER_ID) {
            System.err.println("集群id不合法");
            return -1;
        }
        if(workerId < 0 || workerId > MAX_WORKER_ID) {
            System.err.println("机器id不合法");
            return -1;
        }
        long currentTimeMills = System.currentTimeMillis();
        long ctm = currentTimeMills - INIT_TIMESTAMP;
        long seq;
        if(lastTimeMills > currentTimeMills) {
            System.err.println("系统时钟回拨，无法生成雪花id");
            return -1;
        } else if(lastTimeMills == currentTimeMills){
            seq = sequence++;
            if(seq > MAX_SEQUENCE) {
                long timeMills;
                while((timeMills = System.currentTimeMillis()) == currentTimeMills) ;
                currentTimeMills = timeMills;
                ctm = currentTimeMills - INIT_TIMESTAMP;
                sequence = 0;
                seq = sequence++;
            }
        } else {
            sequence = 0;
            seq = sequence++;
        }
        lastTimeMills = currentTimeMills;
        return (ctm << (DATACENTER_ID_BITS + WORKER_ID_BITS + SEQUENCE_BITS))
                | (dataCenterId << (WORKER_ID_BITS + SEQUENCE_BITS))
                | (workerId << SEQUENCE_BITS)
                | seq;
    }

    public synchronized long nextId() {
        return nextId(DEFAULT_DATACENTER_ID, DEFAULT_WORKER_ID);
    }

    public static void main(String[] args) {
        System.out.println(MAX_SEQUENCE);
        Practice6SnowFlakeUtil snowFlakeUtil = new Practice6SnowFlakeUtil();
        int threadSize = 20;
        int[] workerIds = new int[]{0,1};
        Runnable task = () -> {
            Random random = new Random();
            int randomInt = random.nextInt();
            if(randomInt < 0) randomInt = -randomInt;
            long id = snowFlakeUtil.nextId(0,workerIds[randomInt % 2]);
            System.out.println("[" + reformatThreadName(Thread.currentThread(), threadSize)
                    + "]timestamp="+System.currentTimeMillis()
                    +",id="+id+",binary="+Long.toBinaryString(id)
                    +",workerId=" + workerIds[randomInt % 2]
            );
        };
        List<Thread> threads = new ArrayList<>(threadSize);
        for(int i=0;i<threadSize;i++) {
            threads.add(new Thread(task));
        }
        for(int i=0;i<threadSize;i++) {
            threads.get(i).start();
        }
    }

    private static String reformatThreadName(Thread thread, int threadSize) {
        int n = 0;
        do {
            threadSize /= 10;
            n++;
        } while(threadSize != 0);
        String[] originalName = thread.getName().split("-");
        String newName = originalName[0] + "-";
        for(int i=0;i<(n-originalName[1].length());i++) {
            newName = newName + "0";
        }
        newName = newName + originalName[1];
        return newName;
    }

}